LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

温故知新UE4(网络同步)

2021/12/21

在开发完ue的demo后,回头总结了一些心得,趁着闲暇就记录下来。

这一篇看上去一点也不ue4,等熟了ue4的网络同步再来补相关部分。

TCP三四次握手

创建连接(三次握手)

第一次:客户端尝试连接服务器,向服务器发送syn包(seq=x)。客户端进入SYN_SEND状态。

第二次:服务器接收客户端的syn包,同时向客户端发送一个SYN包(seq=y,ack=x+1),同时发送一个ACK,即发送的是ACK+SYN,服务器进入SYN_RECV状态

第三次:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(seq=x+1,ack=y+1),客户端和服务器进入Establish,TCP连接建立完成。


释放连接(四次握手)

第一次:客户端进程发出连接释放报文FIN,并停止发送数据。客户端进入FIN_WAIT1状态。

第二次:服务器收到连接释放报文,发出确认报文ACK(ack=u+1,seq=v),服务器进入CLOSE_WAIT状态。此时客户端不接受数据,但若服务器发送数据,客户端依然要接受。客户端收到服务器的确认请求,进入FIN_WAIT2状态。

第三次:服务器将最后的数据发送完,向客户端发送释放连接报文FIN(ack=u+1,seq=w),服务器进入LAST_ACK状态。

第四次:客户端收到服务器的连接释放报文,必须发出确认报文ACK(ack=w+1,seq=u+1),客户端进入TIME_WAIT状态,等待2xMSL(最长报文寿命时间),进入CLOSED,服务器收到ACK立即进入CLOSED。

一些之外的思考

为什么建立连接是三次握手,释放连接是四次握手?

因为建立连接时服务器收到客户端的SYN时,可以同时发送一个ACK加SYN。释放连接时由于不知道是否还要继续传输文件,SOCKET没有立即关闭,所以只能发送一个ACK。

为什么TIME_WAIT状态需要经过2MSL才能进入CLOSE?

我们得假设网络是不可靠的。TIME_WAIT用来重发可能丢失的ACK。当服务器没有收到ACK时,会一直重复发送FIN,所以服务端不能关闭,得确保在2MSL内没有收到FIN才能进入CLOSED。其中2MSL是一个发送和一个回复所需的最大时间。

为什么两次握手不能完成TCP连接创建?

若只有两次连接,在服务器传回给客户端的应答丢失的情况下,客户端不知道服务器是否准备好,会认为连接未建立而忽略服务器发来的任何分组,只等待ACK。而服务器会重复发送同样的分组,从而造成死锁。


TCP和UDP的区别

  • TCP面向连接,UDP无连接
  • TCP面向字节流,UDP面向报文
  • TCP提供可靠性(按序发送,有序到达,超时重传),UDP只尽力而为,不保证任何事。
  • TCP首部20字节,所需资源多,而UDP只占用8字节
  • TCP有流量控制和拥塞控制,而UDP没有(拥塞不会影响发送端的发送速率)
  • TCP只支持点对点,而UDP可以一对一,一对多,多对多。

网络同步

RPC

即远程过程调用,本地调用远程提供的函数。因为不是一个内存空间,需要网络来表达调用的语义和数据,而不能直接调用。

带来的问题:Call ID映射(让服务器知道我调用是哪个函数),序列化与反序列化(参数需要通过一种标准化的格式来传输,将对象转换为一系列字节流),网络传输

属性同步

对象A的属性更改了,其他端的对应属性也要更改。一般是服务器更改同步到其他客户端。


帧同步

帧同步只同步操作,大部分逻辑在客户端上运行,服务器主要用于验证和广播,逻辑易实现,数据量少,可重播,一致性好。一个玩家卡了所有人都卡。

状态同步

同步游戏的各种状态。客户端上传操作到服务器,服务器收到后运算结果,把各个状态广播给各个客户端,客户端根据状态展现不同的内容。

状态同步是一种不严谨的同步,对延迟的要求并不高。

对比

帧同步客户端一致性全程一致,状态同步中途可能有变化。

延迟方面帧同步要求搞,状态同步要求低。

流量方面,帧同步的流量与操作数量成正比,而状态同步与物体数量成正比。

服务器的任务上来看,帧同步服务器做的是同步操作,而状态同步的服务器则需要接受输入,进行全局仿真,状态的复制。

客户端的任务上来看,帧同步客户端做的是全局仿真,而状态同步客户端则是做局部游戏世界的展示。

断线重连,状态同步更容易,帧同步要从头开始运行(王者荣耀)。

开发效率上来看,帧同步的开发效率很高,接近单机开发。状态同步需要前后台联合开发。

安全性来看,帧同步安全性低。而状态同步在服务器端安全性高,但客户端有作弊可能。

修复延迟

插值,预测,缓存,延迟补偿


UE4网络同步架构

以CS模型为基础,做了面向对象风格的封装,网络代码和游戏逻辑完全分离,网络同步支持可视化编程,使用UDP协议通信。

为什么使用纯UDP?

TCP的可靠性无法定制,且游戏中许多数据不要求可靠。

TCP与UDP都是基于IP协议,底层会互相干扰,混用也会增加设计复杂度。